/*
 * <one line to give the program's name and a brief idea of what it does.>
 * Copyright (C) 2019
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "datatable.hpp"

#include <cmath>
#include <algorithm>

resource DataTable::dronePrice[tableSize::drone] = {};
resource DataTable::dronePriceTotal[tableSize::drone] = {};
double DataTable::droneStats[tableSize::drone][6] = {};
uint32_t DataTable::dronePower[tableSize::drone] = {};

resource DataTable::upgradePrice[tableSize::upgrade] = {};
resource DataTable::upgradePriceTotal[tableSize::upgrade] = {};
double DataTable::upgradeStats[tableSize::upgrade][4] = {};

double DataTable::aiStats[tableSize::ai][4] = {};
resource DataTable::aiReward[tableSize::ai] = {};
resource DataTable::aiRewardTotal[tableSize::ai] = {};

uint32_t DataTable::trophy[tableSize::ai] = {};
uint32_t DataTable::trophyTotal[tableSize::ai] = {};

uint32_t DataTable::power[tableSize::upgrade] = {};
uint32_t DataTable::powerTotal[tableSize::upgrade] = {};

uint32_t DataTable::trophyPrice[tableSize::trophy] = {};
uint32_t DataTable::trophyPriceTotal[tableSize::trophy] = {};

void DataTable::init() {
	resource totalUpgrade;
	uint32_t totalPower = 0;

	for(uint32_t i = 0; i < tableSize::upgrade; ++i) {
		// get upgrades price and total price
		upgradePrice[i] = DataTable::formulaUpgrade(i);
		// total price
		totalUpgrade += upgradePrice[i];
		upgradePriceTotal[i] = totalUpgrade;
		// wings
		upgradeStats[i][3] = formulaWings(i);
		// weapon
		upgradeStats[i][0] = formulaWeapon(i);
		// shield
		upgradeStats[i][1] = formulaShield(i);
		// hull
		upgradeStats[i][2] = formulaHull(i);

		// ship power
		power[i] = formulaPower(i);
		totalPower += power[i];
		powerTotal[i] = totalPower;
	}

	// drone
	resource totalDrone;

	for(uint32_t i = 0; i < tableSize::drone; ++i) {
		// price
		dronePrice[i] = formulaDrone(i);
		totalDrone += dronePrice[i];
		dronePriceTotal[i] = totalDrone;
		// stats
		formulaDroneValue(i, droneStats[i]);
		// power
		dronePower[i] = formulaPower(i, true);
	}

	// trophy && ai
	uint32_t totalTrophy = 0;
	resource totalReward;

	for(uint32_t i = 0; i < tableSize::ai; ++i) {
		// ai stats
		formulaAiStats(i, aiStats[i]);
		// ai rewards resources
		aiReward[i] = formulaReward(i);
		totalReward += aiReward[i];
		aiRewardTotal[i] = totalReward;
		// trophy reward
		trophy[i] = formulaTrophy(i);
		totalTrophy += trophy[i];
		trophyTotal[i] = totalTrophy;
	}

	// trophy price
	uint32_t totalTrophyPrice = 0;

	for(uint32_t i = 0; i < tableSize::trophy; ++i) {
		trophyPrice[i] = formulaTrophyPrice(i);
		totalTrophyPrice += trophyPrice[i];
		trophyPriceTotal[i] = totalTrophyPrice;
	}
}

// private

resource DataTable::formulaUpgrade(const uint16_t & level) {
	resource s;
	s.imatter = level * 5 * std::ceil((level + 6) * 0.2);
	s.ultium = level * 5 * std::ceil((level + 1) * 0.2);
	return s;
}

resource DataTable::formulaDrone(const uint8_t & level) {
	// TODO remove array
	uint32_t dronePrice[tableSize::drone] = { 0,
	                                          50, 100, 150, 200, 275,
	                                          350, 425, 500, 575, 700,
	                                          825, 950, 1075, 1200, 1400,
	                                          1600, 1800, 2000, 2200, 2500,
	                                          2800, 3100, 3400, 3700, 4125
	                                        };
	resource s;
	s.imatter = dronePrice[level];
	return s;
}

double DataTable::formulaWings(const uint16_t & level) {
	return (10 + 0.8 * level) * (1 + floor(level * 0.2) * 0.05);
}

double DataTable::formulaWeapon(const uint16_t & level) {
	double result = 10;

	for(uint32_t i = 1; i <= level; i++) {
		result += 2.5 + floor(i * 0.2) * 0.125 + (i % 5 == 0 ? 0.375 + 0.625 * i * 0.2 : 0);
	}

	return result;
}

double DataTable::formulaShield(const uint16_t & level) {
	double result = 0;

	for(uint32_t i = 1; i <= level; ++i) {
		result += 10 + floor(i * 0.2) / 2 + (i % 5 == 0 ? i * 0.2 * 2.5 - 0.5 : 0);
	}

	return result;
}

double DataTable::formulaHull(const uint16_t & level) {
	double result = 60;

	for(uint32_t i = 1; i <= level; ++i) {
		result += 20 + (i % 5 == 0 ? 2 + i * 0.2 * 6 : floor(i * 0.2));
	}

	return result;
}

void DataTable::formulaDroneValue(const uint8_t & level, double * data) {
	/**
	 * NOTE
	 * 0 - DEADLY INCREASE
	 * 1 - LEECH
	 * 2 - REFLECTOR
	 * 3 - REGEN
	 * 4 - SHIELD ABSORPTION
	 * 5 - SHIELD PENETRATION
	 * first value - initial value
	 * second value - increaced per level
	 */

	double var[6][2] = { {0.02, 0.01}, {0.04, 0.015}, {0.03, 0.01}, {0.005, 0.002}, {0.03, 0.01}, {0.05, 0.015} };

	for(auto i = 0; i < 6; i++) {
		data[i] = [&level, var, i]() -> double {
			return var[i][0] + var[i][1] * level;
		}();
	}
}

void DataTable::formulaAiStats(const uint16_t & level, double * data) {
	/**
	 * NOTE struct same as for ships
	 * 0 - WEAPON
	 * 1 - SHIELD
	 * 2 - HP
	 * 4 - WINGS
	 * first value - base
	 * second value - increased per level
	 */
	double var[4][2] = { {10, 2.5}, {0, 10}, {60, 20}, {10, 0.8} };

	for(auto i = 0; i < 4; ++i) {
		data[i] = [&level, var, i]() -> double {
			return (var[i][0] + var[i][1] * (level - 1 + std::max(0, (int)level - 500))) * (1 + floor((level - 1) * 0.2) * 0.05);
		}();
	}
}

uint32_t DataTable::formulaTrophy(const uint16_t & level) {
	return std::ceil(0.1 * level);
}

uint32_t DataTable::formulaPower(const uint16_t & level, bool drone) {
	uint32_t power = 0;

	if(level > 0) {
		if(drone) {
			power = 40 + 5 * level;
		} else {
			power = 20 + std::floor(level * 0.2) * 2 + (std::floor(level * 0.2) == level * 0.2 ? (level - 1) * 2 : 0);
		}
	}

	return power;
}

resource DataTable::formulaReward(const uint32_t & level) {
	resource s;
	auto cc = [&level](const uint32_t multiplier) -> uint32_t {
		return multiplier * level * (1 + (level % 5 == 0));
	};
	s.imatter = cc(15);
	s.pcore = cc(25);
	s.ultium = cc(10);
	return s;
}

uint32_t DataTable::formulaTrophyPrice(const uint32_t & level) {
	return 5 * level;
}
